#ifndef		__ENGINE_NETWORK_H_INCLUDED__
#define		__ENGINE_NETWORK_H_INCLUDED__

#include	<cstdint>
#include	<string>

namespace ENet {

	/**
	 * Socket status or error code.
	 **/
	enum Error {
		Success = 0,
		None = 0,
		BadParam = -65536,
		Closed,
		Running,
		Alloc,
		Epoll,
		Timeout,
		TooMany
	};

	/**
	 * Close reason.
	 **/
	enum Close {
		Local,
		Remote,
		BadData
	};
}

class SocketGuard;
class Socket;
class ServerSocket;

/**
 * TCP client side.
 **/
class IConnector {
public:
	IConnector();
	virtual ~IConnector();

	/**
	 * Connect to TCP server.
	 *
	 * \param	sIP		IP address of remote server.
	 * \param	nPort	Port to connect to.
	 * \param	bAutoReconnect	Set 'true' to enable auto-reconnect.
	 * \return	Connect status. See ENet::Error for detail information.
	 **/
	int Connect(const std::string & sIP, int nPort, bool bAutoReconnect = false);

	/**
	 * Check connection status.
	 **/
	bool IsConnected();

	/**
	 * Disconnect from server.
	 **/
	void Close();

	/**
	 * Send data to server.
	 *
	 * \param	pData	Pointer to data buffer.
	 * \param	nSize	Size of data to be sent.
	 * \return	Failed to send data will return false.
	 **/
	bool Send(const char * pData, size_t nSize);

	/**
	 * Process all received data at once. This may invoke OnReceive() many times.
	 * NOTE : Except using Application, you should call this in you main event loop.
	 **/
	void Breath();

	/**
	 * Action to do after connected to server.
	 **/
	virtual void OnConnected() {}

	/**
	 * Action to do when received message from server.
	 *
	 * \param	pData	Pointer to message.
	 * \param	nSize	Size of this message in bytes.
	 **/
	virtual void OnReceive(char * pData, size_t nSize) = 0;

	/**
	 * Action to do when disconnect from server. Detail info will output to logs.
	 *
	 * \param	emCode	Close reason. See ENet::Close for detail information.
	 **/
	virtual void OnClose(ENet::Close emCode) {}

private:
	Socket *		_pCtx;
	SocketGuard *	_pGuard;
};

/**
 * TCP Connection information. Used by server-side socket.
 **/
class Connection {
public:
	Connection(ServerSocket * pOwner, uint64_t nId, int nSocket, uint32_t nIP);

	/**
	 * Get unique id of this connection.
	 */
	uint64_t Id() const { return _nId; }

	/**
	 * Get socket.
	 */
	int Socket() const { return _nSocket; }

	/**
	 * Get remote(client) IP address.
	 **/
	const char * IP() const { return _sIP; }

	/**
	 * Send response to this connection.
	 *
	 * \param	pData	Pointer to data buffer.
	 * \param	nSize	Size of data to be sent.
	 * \return	If successfully send.
	 **/
	bool Send(const char * pData, size_t nSize);

	/**
	 * Close by server side.
	 */
	void Close();

	/**
	 * Attach user data to this connection.
	 *
	 * \param	pUserData	User data.
	 */
	void Attach(void * pUserData) {
		_pUserData = pUserData;
	}

	/**
	 * Get attached user data from this connection.
	 */
	template<typename T>
	T * Attached() {
		if (!_pUserData) return nullptr;
		return (T *)_pUserData;
	}

private:
	ServerSocket *	_pOwner;
	uint64_t		_nId;
	int				_nSocket;
	char			_sIP[16];
	void *			_pUserData;

	friend class ServerSocket;
};

/**
 * TCP server side.
 **/
class IService {
public:
	IService();
	virtual ~IService();

	/**
	 * Listen for connections.
	 *
	 * \param	sIP		Listen IP.
	 * \param	nPort	Port to listen on.
	 * \return	Listen status. See ENet::Error.
	 **/
	int Listen(const std::string & sIP, int nPort);

	/**
	 * Get connection info.
	 *
	 * \param	nConnId	Client identifier generated by OnAccept().
	 * \return	Connection pointer.
	 **/
	Connection * Find(uint64_t nConnId);

	/**
	 * Broadcast message to all connected clients.
	 *
	 * \param	pData	Pointer to data buffer.
	 * \param	nSize	Size of data.
	 **/
	void Broadcast(const char * pData, size_t nSize);

	/**
	 * Shutdown this server.
	 **/
	void Shutdown();

	/**
	 * Process all received data at once. This may invoke OnReceive() many times.
	 * NOTE : Except using Application, you should call this in you main event loop.
	 **/
	void Breath();

	/**
	 * Invoked after a client try to connect to this server.
	 *
	 * \param	pConn	Client information.
	 **/
	virtual void OnAccept(Connection * pConn) {}

	/**
	 * Invoked by OnTick(). Jobs to with message received from client.
	 *
	 * \param	pConn	Client information.
	 * \param	pData	Pointer to message data buffer.
	 * \param	nSize	Message size.
	 **/
	virtual void OnReceive(Connection * pConn, char * pData, size_t nSize) = 0;

	/**
	 * Invoked after a client disconnect with this server. After calling this, pConn will be destoryed.
	 *
	 * \param	pConn	Client information.
	 * \param	emCode	Close reason. See ENet::Close.
	 **/
	virtual void OnClose(Connection * pConn, ENet::Close emCode) {}

	/**
	 * Action to do before server shutdown.
	 **/
	virtual void OnShutdown() {}

private:
	ServerSocket *	_pCtx;
};

#endif//!	__ENGINE_NETWORK_H_INCLUDED__